package com.wdciti.sccp.iot.springvertx.factory;

import com.wdciti.sccp.iot.common.aio.DeployingException;
import com.wdciti.sccp.iot.common.aio.HandlerDeploy;
import com.wdciti.sccp.iot.common.endpoint.EndpointConfig;
import com.wdciti.sccp.iot.springvertx.EndPointVerticle;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Promise;
import io.vertx.core.Verticle;
import io.vertx.core.json.JsonObject;
import io.vertx.core.spi.VerticleFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;

import java.util.Map;
import java.util.concurrent.Callable;

/**
 * @author by early
 */
@Slf4j
public class EndpointBeanVerticleFactory extends AbstractVerticleFactory implements ApplicationContextAware {
    private ApplicationContext context;
//    private final VerticleExclude exclude;

//    public EndpointBeanVerticleFactory(VerticleExclude exclude) {
//        this.exclude = exclude;
//    }

    @Override
    public String prefix() {
        return "endpoint";
    }

    @Override
    public HandlerDeploy prepare() {

        Map<String, EndpointConfig> map = context.getBeansOfType(EndpointConfig.class);

        map.forEach((s, endpointConfig) -> {
            if (endpointConfig.isEnable()) {
                try {
                    String id = endpointConfig.getName();
                    String name = searchBeanName(endpointConfig.getConfigMap());
                    DeploymentOptions options = new DeploymentOptions();
                    JsonObject confJson = JsonObject.mapFrom(endpointConfig.getConfigMap());
                    confJson.put("id", id).remove("epClass");
                    options.setConfig(confJson)
                            .setHa(confJson.getBoolean("isHa"))
                            .setInstances(confJson.getInteger("instance"))
                            .setWorker(confJson.getBoolean("isWorker"));
                    this.willDeploy.put(verticleFullName(name + EndPointVerticle.SPA + id), options);
                } catch (Exception e) {
                    log.error("search verticleBean Error in config[{}], cause: {}", s, e.getMessage());
                }
            }
        });
        return this;
    }

    private String searchBeanName(Map<String, Object> confMap) throws DeployingException {
        String name = "";
        if (confMap.containsKey("epBean")) {
            String epBean = confMap.get("epBean").toString();
            if (context.containsBean(epBean)) {
                name = epBean;
            }
        } else {
            Class<?> epClass = (Class<?>) confMap.get("epClass");
            String[] names = context.getBeanNamesForType(epClass);
            if (names.length == 0) {
                throw new DeployingException("no beans of type @" + epClass.getCanonicalName());
            } else if (names.length > 1) {
                throw new DeployingException("multiple beans of type @" + epClass.getCanonicalName());
            } else {
                name = names[0];
            }
        }
        try {
            context.getBean(name, Verticle.class);
        } catch (BeansException e) {
            throw new DeployingException(name + " is not a Verticle Bean");
        }
        return name;
    }

    /**
     * 按照规则创建Verticle实例对象，这里借助了SpringBean的作用。
     */
    @Override
    public void createVerticle(String verticleName, ClassLoader classLoader, Promise<Callable<Verticle>> promise) {
        String name = VerticleFactory.removePrefix(verticleName);
        String[] shot = name.split(EndPointVerticle.SPA);
//        if(exclude.isExclude(s))
        Verticle verticle = context.getBean(shot[0], Verticle.class);
        promise.complete(() -> verticle);
    }

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }
}
